package com.gec.controllers;

import cn.hutool.core.util.StrUtil;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.gec.entities.Class;
import com.gec.pagination.PagedResult;
import io.quarkus.hibernate.orm.panache.PanacheQuery;
import io.quarkus.panache.common.Page;
import io.quarkus.panache.common.Sort;
import jakarta.annotation.security.RolesAllowed;
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.inject.Inject;
import jakarta.transaction.Transactional;
import jakarta.ws.rs.*;
import jakarta.ws.rs.core.MediaType;
import jakarta.ws.rs.core.Response;
import jakarta.ws.rs.ext.ExceptionMapper;
import jakarta.ws.rs.ext.Provider;
import org.jboss.logging.Logger;

import java.util.*;

import static com.gec.Generics.GenericHelper.updateEntity;


@Path("class")
@ApplicationScoped
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON)
@RolesAllowed({"VIEW_ADMIN_DETAILS"})
public class ClassController {

    private static final Logger LOGGER = Logger.getLogger(ClassController.class.getName());

    /**
     * 列出所有班级信息和分页
     *
     * @param page     页码，从0开始。
     * @param size     每页大小。
     * @param sort     排序字段。
     * @param className    班级名称过滤条件。
     * @param schedule     授课时间过滤条件。
     * @param teacherId    老师ID过滤条件。
     * @return 响应结果。
     */

    @GET
    public Response listAll(
            @QueryParam("page") Integer page,
            @QueryParam("size") Integer size,
            @QueryParam("sort") String sort,
            @QueryParam("className") String className,
            @QueryParam("schedule") String schedule,
            @QueryParam("teacherId") Long teacherId
    ) {
        // 初始化默认的分页和排序参数
        size = (size == null) ? 10 : size;
        page = (page == null) ? 0 : page;
        sort = (Objects.equals(sort, "")) ? "className" : sort;

        // 确保排序字段有效
        String validSortColumn = "className";
        List<String> validSortColumns = Arrays.asList("className", "schedule", "teacherId");
        if (validSortColumns.contains(sort)) {
            validSortColumn = sort;
        }

        // 构建基础查询语句
        String queryStr = "FROM Class WHERE 1=1";
        Map<String, Object> params = new HashMap<>();

        // 根据条件构建查询语句
        if (StrUtil.isNotBlank(className)) {
            queryStr += " AND LOWER(className) LIKE LOWER(CONCAT('%', :className, '%'))";
            params.put("className", className);
        }
        if (StrUtil.isNotBlank(schedule)) {
            queryStr += " AND LOWER(schedule) LIKE LOWER(CONCAT('%', :schedule, '%'))";
            params.put("schedule", schedule);
        }
        if (teacherId != null) {
            queryStr += " AND teacher.id = :teacherId";
            params.put("teacherId", teacherId);
        }

        // 创建并执行查询
        PanacheQuery<Class> query = Class.find(queryStr, Sort.by(validSortColumn), params);
        Long totalCount = query.count();
        List<Class> classes = query.page(Page.of(page, size)).list();

        // 构建并返回分页结果
        return Response.ok(
                new PagedResult<>(
                        classes,
                        totalCount,
                        (int) Math.ceil((double) totalCount / size),
                        page > 0,
                        (page + 1) * size < totalCount
                )
        ).build();
    }

    /**
     * 获取单个班级信息。
     *
     * @param id 班级ID。
     * @return 班级实体。
     * @throws WebApplicationException 如果指定ID的班级不存在。
     */
    @Path("{id}")
    public Class getSingle(Long id) {
        Class entity = Class.findById(id);
        if (entity == null) {
            throw new WebApplicationException("Class with id of " + id + " does not exist.", 404);
        }
        return entity;
    }

    /**
     * 创建新的班级。
     *
     * @param entity 新班级实体。
     * @return 创建成功后的班级实体。
     * @throws WebApplicationException 如果请求体中设置了ID。
     */
    @POST
    @Transactional
    public Response create(Class entity) {
        if (entity.id != null) {
            throw new WebApplicationException("Id was invalidly set on request.", 422);
        }

        entity.persist();
        return Response.ok(entity).status(201).build();
    }

    /**
     * 更新班级信息。
     *
     * @param id 班级ID。
     * @param updatedClass 更新后的班级实体。
     * @return 更新成功后的班级实体。
     * @throws WebApplicationException 如果指定ID的班级不存在。
     */
    @PUT
    @Path("{id}")
    @Transactional
    public Response updateClass(@PathParam("id") Long id, Class updatedClass) {
        Class existingClass = Class.findById(id);
        if (existingClass == null) {
            throw new WebApplicationException("Class with id " + id + " does not exist.", 404);
        }

        updateEntity(existingClass, updatedClass);

        return Response.ok(existingClass).build();
    }

    /**
     * 删除班级。
     *
     * @param id 班级ID。
     * @return 无内容的响应，状态码为204。
     * @throws WebApplicationException 如果指定ID的班级不存在。
     */
    @DELETE
    @Path("{id}")
    @Transactional
    public Response delete(Long id) {
        Class entity = Class.findById(id);
        if (entity == null) {
            throw new WebApplicationException("Class with id of " + id + " does not exist.", 404);
        }
        entity.delete();
        return Response.status(204).build();
    }

    /**
     * ErrorMapper 类用于全局异常处理。
     * 它将所有未被捕获的异常映射为HTTP响应。
     */
    @Provider
    public static class ErrorMapper implements ExceptionMapper<Exception> {

        @Inject
        ObjectMapper objectMapper;

        /**
         * 将异常转换为HTTP响应。
         *
         * @param exception 被捕获的异常。
         * @return 包含异常信息的HTTP响应。
         */
        @Override
        public Response toResponse(Exception exception) {
            LOGGER.error("Failed to handle request", exception);

            // 确定HTTP状态码
            int code = 500;
            if (exception instanceof WebApplicationException) {
                code = ((WebApplicationException) exception).getResponse().getStatus();
            }

            // 构建异常信息的JSON对象
            ObjectNode exceptionJson = objectMapper.createObjectNode();
            exceptionJson.put("exceptionType", exception.getClass().getName());
            exceptionJson.put("code", code);

            if (exception.getMessage() != null) {
                exceptionJson.put("error", exception.getMessage());
            }

            return Response.status(code)
                    .entity(exceptionJson)
                    .build();
        }

    }
}
